package com.flong.utils;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;


/**
 * @Description 
 * ==========================================================================================
 * 参考博客：http://www.cnblogs.com/whoislcj/p/5526431.html
 * 		  http://www.cnblogs.com/yinxiaoqiexuxing/p/5605338.html
 * ==========================================================================================
 * 秒的换算：ms(毫秒)millisecond ,μs(微秒)microsecond ,ns(纳秒)nanosecond ,ps(皮秒)picosecond  
 * 秒的换算  http://blog.chinaunix.net/uid-28458801-id-4144886.html
 * Semaphored的使用： http://www.cnblogs.com/whgw/archive/2011/09/29/2195555.html 
 * ==========================================================================================
 * maven导入okhttp与slf4j的jar 浏览器打开maven仓库输入okhttp和slf4j即可,如下：
 * 仓库官方网:---->>http://mvnrepository.com/
 * 
 * <dependency>
 *		    <groupId>com.squareup.okhttp3</groupId>
 *		    <artifactId>okhttp</artifactId>
 *		    <version>3.9.1</version>
 *	</dependency>
 * ==========================================================================================
 * @ClassName   OkHttpClientUtil  
 * @Date        2017年7月10日 下午5:39:49  
 * @Author      liangjilong  
 * @Copyright (c) All Rights Reserved, 2017.
 */
@SuppressWarnings("all")
public class OkHttpClientUtil {
	
	private static Logger logger = LoggerFactory.getLogger(OkHttpClientUtil.class);
	//private static String JSON = "application/json; charset=utf-8";
	private static String MEDIA_TYPE_JSON= "application/x-www-form-urlencoded; charset=utf-8";
	/**使用volatile双重校验锁**/
	private static volatile Semaphore semaphore = null;
	private static volatile OkHttpClient okHttpClient = null; 
	
	/**建立单例模式*/
	public static  Semaphore getSemaphoreInstance(){
		//只能0个线程同时访问
		synchronized (OkHttpClientUtil.class) {
			if (semaphore == null) {
				semaphore = new Semaphore(0);
			}
		}
		return semaphore;
	}
	
	
	/**建立单例模式*/
	public static  OkHttpClient getInstance(){
		synchronized (OkHttpClientUtil.class) {
			if (okHttpClient == null) {
				//这里是以毫秒为单位，1000 毫秒 = 1秒
				okHttpClient = new OkHttpClient().newBuilder()
						.connectTimeout(5000, TimeUnit.SECONDS)// 设置超时时间
						.readTimeout(5000, TimeUnit.SECONDS)// 设置读取超时时间
						.writeTimeout(5000, TimeUnit.SECONDS)// 设置写入超时时间
						.build();
			}   
		}
		return okHttpClient;
	}
	
	
	public static void main(String[] args) throws Exception{
		/**组装参数*/
		Map<String, Object> reqMap = new HashMap<String, Object>();
		reqMap.put("id", "1");
		
		//============================测试1==============================
		//String url = "http://localhost:8080/queryUserInfo?"+concatParams(reqMap).toString();
		//String retMsg = createAsycHttpPost(url,null,MEDIA_TYPE_JSON);
		
		
		//============================测试2==============================
		
		/*String url = "http://localhost:8080/queryUserInfo";
		createAsycHttpGet(url, reqMap, MEDIA_TYPE_JSON, new IOkHttpClientCallBack() {
			@Override
			public void onSuccessful(String retBody) {
				System.out.println(retBody);
			}
		});
		*/
		
		//============================测试3==============================
	 	/*String url = "http://localhost:8080/queryUserInfo";
	 	String retMsg = createHttpGet(url, reqMap, MEDIA_TYPE_JSON);
		
	 	System.out.println(retMsg);*/
		
		
		
		//============================测试4==============================
	 	/*String url = "http://localhost:8080/queryUserInfo";
	 	String retMsg = createAsycHttpPost(url, reqMap, MEDIA_TYPE_JSON);
		
	 	System.out.println(retMsg);*/
		
		//============================测试5==============================
		
		/*
		String url = "http://localhost:8080/queryUserInfo";
		String retMsg = createPostByAsynWithForm(url, reqMap);
		System.out.println(retMsg);*/
		
		
		//============================测试6==============================
		/*
		String url = "http://localhost:8080/queryUserInfo";
		String retMsg = createPostByAsynWithForm(url, reqMap);
		System.out.println(retMsg);*/
		
		
		//============================测试7==============================
		String url = "https://www.12306.cn/mormhweb/";
		String retMsg = createHttpsPost(url,null,MEDIA_TYPE_JSON);
	    System.out.println(retMsg);
		
	}
	
 
	/**
     * @Description 求在子线程发起网络请求
     * @Author      liangjilong  
     * @Date        2017年7月10日 下午3:53:49  
     * @param url 请求url地址
     * @param params  请求body参数
     * @param okHttpClientCall 回调接口
     * @throws IOException 参数  
     * @return void 返回类型
	 */
	public  static void createAsycHttpGet(String url,Map<String,Object> params,String contentType,final IOkHttpClientCallBack okHttpClientCall)  {
		// 创建请求对象
		Call call = createCall(url, params);
		
		//发起异步的请求
 		call.enqueue(new Callback() {
			@Override
			public void onResponse(Call call, Response response) throws IOException {
				if (response!=null && response.isSuccessful()) {
					String string = response.body().string();
					okHttpClientCall.onSuccessful(string);
				}
			}
			@Override
			public void onFailure(Call call, IOException e) {
				String errorLog = getCurrentClassName()+"#createHttpGet,请求异常，异常信息为:"+e.getMessage();
				logger.error("@see "+errorLog);
			}
		});
	}
	
	/**
     * @Description 求在子线程发起网络请求
     * @Author      liangjilong  
     * @Date        2017年7月10日 下午3:53:49  
     * @param url 请求url地址
     * @param params  请求body参数
     * @param okHttpClientCall 回调接口
     * @throws IOException 参数  
     * @return void 返回类型
	 */
	public static String createAsycHttpGet(String url,Map<String,Object> params,String contentType)  {
		final StringBuilder buffer = new StringBuilder("");
		try {
			// 创建请求对象
			Call call = createCall(url, params);
			
			//发起异步的请求
			call.enqueue(new Callback() {
				@Override
				public void onResponse(Call call, Response response) throws IOException {

					if (response!=null && response.isSuccessful()) {
						String string = response.body().string();
						buffer.append(string);
						getSemaphoreInstance().release();
					}
				}
				@Override
				public void onFailure(Call call, IOException e) {
					String errorLog = getCurrentClassName()+"#createHttpGet,请求异常，异常信息为:"+e.getMessage();
					logger.error("@see "+errorLog);
				}
			});
			getSemaphoreInstance().acquire();//获取许可
			return buffer.toString();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return buffer.toString();
	}
	
	/**
     * @Description 求在子线程发起网络请求
     * @Author      liangjilong  
     * @Date        2017年7月10日 下午3:53:49  
     * @param url 请求url地址
     * @param params  请求body参数
     * @param okHttpClientCall 回调接口
     * @throws IOException 参数  
     * @return void 返回类型
	 */
	public static String createHttpGet(String url,Map<String,Object> params,String contentType)  {
		try {
			// 创建请求对象
			Call call = createCall(url, params);
			
			Response response = call.execute();
			if (response!=null && response.isSuccessful() && ObjectUtil.isNotEmpty(response.body())) {
				//Collection<String> readLines = IOUtil.readLines(byteStream);
				//System.out.println(readLines);
				return convertToString(response.body().byteStream());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
	
	
	/**
	 * @Description convertStreamToString
	 * @Author		liangjl
	 * @Date		2018年2月9日 下午3:02:00
	 * @param is
	 * @return 参数
	 * @return String 返回类型 
	 * @throws
	 */
	public static String convertToString(InputStream is) {
		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		StringBuilder buffer = new StringBuilder();
		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				buffer.append(line + "\r");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return buffer.toString();
	}
	
	/**
	 * @Description convertStr
	 * @Author		liangjl
	 * @Date		2018年2月9日 下午3:01:49
	 * @param is
	 * @return
	 * @throws IOException 参数
	 * @return String 返回类型 
	 * @throws
	 */
	public static String convertStr(InputStream is) throws IOException {
		OutputStream baos = new ByteArrayOutputStream();
		int i = -1;
		while ((i = is.read()) != -1) {
			baos.write(i);
		}
		return baos.toString();
	}
	/**
     * @Description 创建异步表单Body参数的post请求处理  
     * @Author      liangjilong  
     * @Date        2017年7月11日 上午9:46:04  
     * @param url   请求链接
     * @param params 请求表单body参数
     * @param okHttpClientCall 参数  回调接口
     * @return void 返回类型
	 */
	public static void createPostByAsynWithForm(String url,Map<String,Object> params,final IOkHttpClientCallBack okHttpClientCall)  {
		FormBody.Builder builder = new FormBody.Builder();
        for (String key : params.keySet()) {
            builder.add(key, params.get(key).toString());
        }
        RequestBody formBody = builder.build();
        logger.info("@see"+getCurrentClassName()+"请求url"+url+",请求参数:"+formBody);
        
        Request request = new Request.Builder().url(url).post(formBody).build();
        // 创建请求对象
 		Call call = getInstance().newCall(request);
 		//发起异步的请求
 		call.enqueue(new Callback() {
			@Override
			public void onResponse(Call call, Response response) throws IOException {

				if (response!=null && response.isSuccessful()) {
					String string = response.body().string();
					okHttpClientCall.onSuccessful(string);
				}
			}
			
			@Override
			public void onFailure(Call call, IOException e) {
				String errorLog = getCurrentClassName()+"#createPostByAsynWithForm,请求异常，异常信息为:"+e.getMessage();
				//okHttpClientCall.onFailure(errorLog);
				logger.error("@see "+errorLog);
			}
		});
	}
	
	
	/**
     * @Description 创建异步表单Body参数的post请求处理  
     * @Author      liangjilong  
     * @Date        2017年7月11日 上午9:46:04  
     * @param url   请求链接
     * @param params 请求表单body参数
     * @param okHttpClientCall 参数  回调接口
	 * @return 
     * @return void 返回类型
	 */
	public static String createPostByAsynWithForm(String url,Map<String,Object> params)  {
		final StringBuilder buffer = new StringBuilder("");
		try {
			FormBody.Builder builder = new FormBody.Builder();
			for (String key : params.keySet()) {
			    builder.add(key, params.get(key).toString());
			}
			RequestBody formBody = builder.build();
			logger.info("@see"+getCurrentClassName()+"请求url"+url+",请求参数:"+formBody);
			
			Request request = new Request.Builder().url(url).post(formBody).build();
			// 创建请求对象
			Call call = getInstance().newCall(request);
			//发起异步的请求
			call.enqueue(new Callback() {
				@Override
				public void onResponse(Call call, Response response) throws IOException {

					if (response!=null && response.isSuccessful()) {
						String string = response.body().string();
						buffer.append(string);
						getSemaphoreInstance().release();
					}
				}
				
				@Override
				public void onFailure(Call call, IOException e) {
					String errorLog = getCurrentClassName()+"#createPostByAsynWithForm,请求异常，异常信息为:"+e.getMessage();
					logger.error("@see "+errorLog);
				}
			});
			getSemaphoreInstance().acquire();
			return buffer.toString();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return buffer.toString();
	}
	
	 
	
	/**
     * okHttp createCall
     * @param url  接口地址
     * @param params   请求参数
     */
    private static Call createCall(String url, Map<String, Object> params) {
        //补全请求地址,【%s?%s或者%s/%s的使用】第一个%s代表第一个参数，第二个?代表是请求地址的?后面%s代表是组装戳参数,如：
    	//http://localhost:8080/api/test.do?userId=1212&deviceInfo=PC
        String requestUrl = String.format("%s?%s", url, concatParams(params).toString());
        //创建一个请求
        Request request = new Request.Builder().url(requestUrl).build();
       return  getInstance().newCall(request);
    }
	
    
    /**
     * @Description createHttpPost  
     * @Author      liangjilong  
     * @Date        2017年7月11日 下午12:20:04  
     * @param url
     * @param reqMap
     * @param contentType
     * @return 参数  
     * @return String 返回类型
     */
    public static String createHttpPost(String url,Map<String,Object> reqMap,String contentType) {
    	try {
    		RequestBody body = createRequestBody(contentType, reqMap);
    		//logger.info("@see"+getCurrentClassName()+"#createHttpPost,请求url"+url+",请求参数:"+body.toString());
    		final Request request = new Request.Builder().url(url).post(body).build();
    		// 创建请求对象
    		final Call call = getInstance().newCall(request);
			Response response = call.execute();
			if (response!=null && response.isSuccessful()) {
				return convertStr(response.body().byteStream());
			} 
		} catch (IOException e) {
			e.printStackTrace();
		}
    	return "";
			
    }
		
	/**
     * @Description 在子线程发起 post 请求  
     * @Author      liangjilong  
     * @Date        2017年7月10日 下午3:58:39  
     * @param url 参数  
     * @return void 返回类型
	 */
	public static String createAsycHttpPost(String url,Map<String,Object> reqMap,String contentType) {
		final StringBuilder buffer = new StringBuilder("");
		try {
			final RequestBody body = createRequestBody(contentType, reqMap);
			
			//logger.info("@see"+getCurrentClassName()+"#createHttpPost,请求url"+url+",请求参数:"+body.toString());
			final Request request = new Request.Builder().url(url).post(body).build();
			// 创建请求对象
			final Call call = getInstance().newCall(request);
			
			// 发起请求
			call.enqueue(new Callback() {
			    @Override
			    public void onFailure(Call call, IOException e) {
			    	String errorLog = getCurrentClassName()+"#createHttpPost,请求异常，异常信息为:"+e.getMessage();
					logger.error("@see "+errorLog);
			    }
			    @Override
			    public void onResponse(Call call, Response response) throws IOException {
			         if (response!=null && response.isSuccessful()) {
			    	    if(ObjectUtil.isNotEmpty(response.body())){
			    	    	String string = response.body().string();
			    	    	buffer.append(string);
			    	    	getSemaphoreInstance().release();//释放
			    	    }
					 }
			    }
			  });
			getSemaphoreInstance().acquire();//接受
			return  buffer.toString();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return buffer.toString();
	}
  
	/**
     * @Description 在子线程发起 post 请求  
     * @Author      liangjilong  
     * @Date        2017年7月10日 下午3:58:39  
     * @param url 参数  
     * @return void 返回类型
	 */
	public static void createAsycHttpPost(String url,Map<String,Object> reqMap,String contentType,final IOkHttpClientCallBack okHttpClientCall) {
		final RequestBody body = createRequestBody(contentType, reqMap);
		
		//logger.info("@see"+getCurrentClassName()+"#createHttpPost,请求url"+url+",请求参数:"+body.toString());
		final Request request = new Request.Builder().url(url).post(body).build();
		// 创建请求对象
		final Call call = getInstance().newCall(request);
		
		// 发起请求
		call.enqueue(new Callback() {
		    @Override
		    public void onFailure(Call call, IOException e) {
		    	String errorLog = getCurrentClassName()+"#createHttpPost,请求异常，异常信息为:"+e.getMessage();
				logger.error("@see "+errorLog);
		    }
		    @Override
		    public void onResponse(Call call, Response response) throws IOException {
		         if (response!=null && response.isSuccessful()) {
		    	    if(ObjectUtil.isNotEmpty(response.body())){
		    	    	String retBody = response.body().string();
		    	    	okHttpClientCall.onSuccessful(retBody);
		    	    }
				 }
		    }
		  });
	}
  
	/**
	 * 
     * @Description 组装参数  
     * @Author      liangjilong  
     * @Date        2017年7月10日 下午5:48:13  
     * @param contentType  请求头header属性
     * @param params       请求参数
     * @return 参数  
     * @return RequestBody 返回类型
	 */
	private static RequestBody createRequestBody(String contentType,Map<String,Object> params){
		MediaType type = MediaType.parse(contentType);
        String paramStrs = concatParams(params).toString();
        return RequestBody.create(type, paramStrs);
	}



	/**
     * @Description 拼接参数  
     * @Author      liangjilong  
     * @Date        2017年7月11日 上午9:34:00  
     * @param params
     * @return 参数  
     * @return StringBuilder 返回类型
	 */
	private static StringBuilder concatParams(Map<String, Object> params) {
		StringBuilder builder = new StringBuilder("");//请求参数为空给一个默认值空字符串
		//判断是空
		if (ObjectUtil.isNotEmpty(params)) {
            int i = 0;
            for (String key : params.keySet()) {
                Object value = params.get(key);
                builder.append(i != 0 ? "&" : "");
                builder.append(key + "=" + value);
                i++;
            }
        }
		return builder;
	}
 
	
	/**
	 * @Description 创建支持https请求
	 * @Author		liangjl
	 * @Date		2018年2月9日 下午3:25:47
	 * @param url
	 * @param reqMap
	 * @param contentType 参数
	 * @return void 返回类型 
	 * @throws
	 */
	 public static String createHttpsPost(String url,Map<String,Object> reqMap,String contentType) {
		final StringBuilder buffer = new StringBuilder("");
		 /**忽略SSL协议证书*/
		
        OkHttpClient build = new OkHttpClient.Builder().sslSocketFactory(createSSLSocketFactory()).hostnameVerifier(new TrustAllHostnameVerifier()).build();
        
        final RequestBody body = createRequestBody(contentType, reqMap);
        
        final Request request  = new Request.Builder().url(url).post(body).build();
        final Call    call     = build.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException
            {
                String res = response.body().string();
                buffer.append(res);
    	    	getSemaphoreInstance().release();//释放
            }
        });
        
        try {
			getSemaphoreInstance().acquire();
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		}
		return  buffer.toString();
    }
	
	
	/**
	 * @Description	TrustAllCerts
	 * @ClassName	TrustAllCerts
	 * @Date		2018年2月9日 下午3:15:23
	 * @Author		liangjl
	 * @Copyright (c) All Rights Reserved, 2018.
	 */
	private static class TrustAllCerts implements X509TrustManager {
		@Override
		public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}

		@Override
		public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
		}

		@Override
		public X509Certificate[] getAcceptedIssuers() {
			return new X509Certificate[0];
		}
	}

	/**
	 * @Description	验证所有主机
	 * @ClassName	TrustAllHostnameVerifier
	 * @Date		2018年2月9日 下午3:15:34
	 * @Author		liangjl
	 * @Copyright (c) All Rights Reserved, 2018.
	 */
	private static class TrustAllHostnameVerifier implements HostnameVerifier {
		@Override
		public boolean verify(String hostname, SSLSession session) {
			return true;
		}
	}

	/**
	 * @Description createSSLSocketFactory
	 * @Author		liangjl
	 * @Date		2018年2月9日 下午3:15:47
	 * @return 参数
	 * @return SSLSocketFactory 返回类型 
	 * @throws
	 */
	private static SSLSocketFactory createSSLSocketFactory() {
		SSLSocketFactory ssfFactory = null;
		try {
			SSLContext sc = SSLContext.getInstance("TLS");
			sc.init(null, new TrustManager[] { new TrustAllCerts() }, new SecureRandom());
			ssfFactory = sc.getSocketFactory();
		} catch (Exception e) {
		}
		return ssfFactory;
	}
	
	/**
	 * 
     * @Description 获取当前类名包含有包名路径  
     * @Author      liangjilong  
     * @Date        2017年5月24日 上午10:33:49  
     * @param @return 参数  
     * @return String 返回类型   
     * @throws
	 */
	public static String getCurrentClassName(){
		return OkHttpClientUtil.class.getName();
	}

	
	/**
	 * @Description	定义一个回调成功的接口.
	 * @ClassName	IOkHttpClientCallBack
	 * @Date		2018年2月9日 下午3:34:18
	 * @Author		liangjl
	 * @Copyright (c) All Rights Reserved, 2018.
	 */
	public interface IOkHttpClientCallBack {

		void onSuccessful(String retBody);

	}
}

 
